#include <cassert>
#include <QDateTime>
#include <QDir>
#include <QFile>
#include <QLocale>
#include <QNetworkInterface>
#include <QTimer>
#include <signal.h>
#include <QCryptographicHash>

#include "dbmysql.h"
#include "dbsqlite.h"
#include "hbot_class.h"
#include "hbot.h"
#include "operationthread.h"
#include "sdefs.h"
#include "xmlconfig.h"

CHBot::CHBot(const QString &profile)
{
	m_Error = HBOTCLASS_ERROR_SUCCESS;
	m_Exiting = false;
	m_ExitingNice = false;
	m_Profile = profile;

	g_HBot.CreateProfileDirectory(m_Profile);

	m_Log = NULL;
	m_LogDevice = NULL;
	m_LogFile = g_HBot.GetXMLProfile()->GetProfileValue(QString::null, m_Profile, TAG_PROFILE_PROFILE, TAG_PROFILE_PROFILE_LOGFILE, ATTRIB_PROFILE_PROFILE_NAME);

	if (m_LogFile.isEmpty())
	{
		m_LogMethod = LOGGING_FILE_EMPTY;
	}
	else
	{
		m_LogFile = "Data/Profiles/" + m_Profile + "/Logs/" + m_LogFile;
		m_LogMethod = g_HBot.GetXMLProfile()->GetProfileValue("1", m_Profile, TAG_PROFILE_PROFILE, TAG_PROFILE_PROFILE_LOGMETHOD).toUInt();
	}

	if (m_LogMethod == LOGGING_DISABLED)
	{
		g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_LOG, "Specified log method is 'Logging Disabled' - Logging disabled.");
	}
	else if (m_LogMethod == LOGGING_FILE_EMPTY)
	{
		m_LogMethod = LOGGING_DISABLED;
		g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_LOG, "Specified log file is empty - Logging disabled.");
	}
	else if (m_LogMethod == LOGGING_METHOD_2)
	{
		g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_LOG, "Logging enabled - Using unlocked log method with log file [" + m_LogFile + "] - Log file is unlocked.");
	}
	else
	{
		m_LogMethod = LOGGING_METHOD_1;
		m_LogDevice = new QFile(m_LogFile);

		if (!m_LogDevice->open(QIODevice::WriteOnly | QIODevice::Append))
		{
			m_LogMethod = LOGGING_DISABLED;
			g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_LOG, "Error: Failed to open log file [" + m_LogFile + "] - " + m_LogDevice->errorString() + " Logging disabled.", QString::null, true);
			delete m_LogDevice;
			m_LogDevice = NULL;
		}
		else
		{
			m_Log = new QTextStream(m_LogDevice);
			g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_LOG, "Logging enabled - Using locked log method with log file [" + m_LogFile + "] - Log file is locked.");
		}
	}

	signal(SIGINT, SignalInt);

#ifndef WIN32
	signal(SIGPIPE, SIG_IGN);
#endif // WIN32 

	m_Time.start();

	m_Database = NULL;
	QString databaseType = g_HBot.GetXMLProfile()->GetProfileValue("SQLite3", m_Profile, TAG_PROFILE_DATABASE, TAG_PROFILE_DATABASE_TYPE, ATTRIB_PROFILE_DATABASE_NAME);

	if (databaseType == "MySQL")
	{
		m_DatabaseType = 1;

		g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_CONFIG, "Opening MySQL database connection (May take a few seconds...).");

		QString server = g_HBot.GetXMLProfile()->GetProfileValue(QString::null, m_Profile, TAG_PROFILE_DATABASE, TAG_PROFILE_DATABASE_MYSQL, ATTRIB_PROFILE_DATABASE_MYSQL_SERVER);

		if (server.isEmpty())
		{
			m_DatabaseType = -1;
			m_Error = HBOTCLASS_ERROR_DATABASE_MYSQL_SERVEREMPTY;
			g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_CONFIG, HBOTCLASS_ERRORSTRING_DATABASE_MYSQL_SERVEREMPTY, QString::null, true);
			return;
		}
		else
		{
			QString databaseName = g_HBot.GetXMLProfile()->GetProfileValue(QString::null, m_Profile, TAG_PROFILE_DATABASE, TAG_PROFILE_DATABASE_MYSQL, ATTRIB_PROFILE_DATABASE_MYSQL_DATABASE);

			if (databaseName.isEmpty())
			{
				m_DatabaseType = -1;
				m_Error = HBOTCLASS_ERROR_DATABASE_MYSQL_DATABASEEMPTY;
				g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_CONFIG, HBOTCLASS_ERRORSTRING_DATABASE_MYSQL_DATABASEEMPTY, QString::null, true);
				return;
			}
			else
			{
				QString user = g_HBot.GetXMLProfile()->GetProfileValue(QString::null, m_Profile, TAG_PROFILE_DATABASE, TAG_PROFILE_DATABASE_MYSQL, ATTRIB_PROFILE_DATABASE_MYSQL_USER);
				
				if (user.isEmpty())
				{
					m_DatabaseType = -1;
					m_Error = HBOTCLASS_ERROR_DATABASE_MYSQL_USEREMPTY;
					g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_CONFIG, HBOTCLASS_ERRORSTRING_DATABASE_MYSQL_USEREMPTY, QString::null, true);
					return;
				}
				else
				{
					QString port = g_HBot.GetXMLProfile()->GetProfileValue(QString::null, m_Profile, TAG_PROFILE_DATABASE, TAG_PROFILE_DATABASE_MYSQL, ATTRIB_PROFILE_DATABASE_MYSQL_PORT);

					if (port.isEmpty())
					{
						m_DatabaseType = -1;
						m_Error = HBOTCLASS_ERROR_DATABASE_MYSQL_PORTEMPTY;
						g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_CONFIG, HBOTCLASS_ERRORSTRING_DATABASE_MYSQL_PORTEMPTY, QString::null, true);
						return;
					}
					else if (port.toUInt() < 0 || port.toUInt() > 65535)
					{
						m_DatabaseType = -1;
						m_Error = HBOTCLASS_ERROR_DATABASE_MYSQL_PORTINVALID;
						g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_CONFIG, HBOTCLASS_ERRORSTRING_DATABASE_MYSQL_PORTINVALID, QString::null, true);
					}
					else
					{
						m_Database = new CDBMySQL(server, databaseName, user, g_HBot.GetXMLProfile()->GetProfileValue(QString::null, m_Profile, TAG_PROFILE_DATABASE, TAG_PROFILE_DATABASE_MYSQL, ATTRIB_PROFILE_DATABASE_MYSQL_PASSWORD), port.toUInt());
						m_Database->start();
					}
				}
			}
		}
	}
	else
	{
		m_DatabaseType = 0;

		g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_CONFIG, "Opening SQLite database connection.");

		QString databaseFile = g_HBot.GetXMLProfile()->GetProfileValue(QString::null, m_Profile, TAG_PROFILE_DATABASE, TAG_PROFILE_DATABASE_SQLITE, ATTRIB_PROFILE_DATABASE_SQLITE_FILENAME);
		
		if (databaseFile.isEmpty())
		{
			m_DatabaseType = -1;
			m_Error = HBOTCLASS_ERROR_DATABASE_SQLITE_FILENAMEEMPTY;
			g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_CONFIG, HBOTCLASS_ERRORSTRING_DATABASE_SQLITE_FILENAMEEMPTY, QString::null, true);
			return;
		}
		else if (g_HBot.ContainsInvalidCharacters(databaseFile))
		{
			m_DatabaseType = -1;
			m_Error = HBOTCLASS_ERROR_DATABASE_SQLITE_FILENAMEINVALID;
			g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_CONFIG, HBOTCLASS_ERRORSTRING_DATABASE_SQLITE_FILENAMEINVALID, QString::null, true);
			return;
		}
		else
		{
			m_Database = new CDBSQLite("Data/Profiles/" + m_Profile + "/Database/" + databaseFile);
		
			if (!m_Database->GetError().isEmpty())
			{
				m_Error = HBOTCLASS_ERROR_DATABASE_SQLITE_ERROR;
				m_DatabaseType = -1;
				return;
			}

			g_HBot.SetOpenDatabase(true);
		}
	}

	g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_CONFIG, "Detecting local IP addresses.");

	// Detect local IP addresses

	QList<QHostAddress> ipList = QNetworkInterface::allAddresses();

	for (int i = 0; i < ipList.count(); ++i)
	{
		if (ipList.at(i) != QHostAddress::LocalHost && ipList.at(i).toIPv4Address())
		{
			g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_CONFIG, " - " + ipList.at(i).toString());
			m_LocalAddresses.push_back(ipList.at(i));
		}
	}

	// Set emulation type (RoC or TFT)

	m_UsingTFT = g_HBot.GetXMLProfile()->GetProfileValue("1", m_Profile, TAG_PROFILE_PROFILE, TAG_PROFILE_PROFILE_USINGTFT).toUInt();

	if (m_UsingTFT)
	{
		g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_CONFIG, "Emulating as Warcraft III: The Frozen Throne");
	}
	else
	{
		g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_CONFIG, "Emulating as Warcraft III: Reign of Chaos");
	}

	// Load Battle.net configuration values

	g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_BNET, "Initializing Battle.net configuration settings.");

	QVector<int> battlenetServers = g_HBot.GetXMLProfile()->GetNumberBnet(m_Profile);

	for (int i = 0; i < battlenetServers.size(); ++i)
	{
		g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_BNET, "Checking validity of settings for Battle.net-" + QString::number(battlenetServers[i] + 1) + " server configuration.", QString::number(battlenetServers[i] + 1));
		QString group = TAG_PROFILE_BATTLENET + QString::number(battlenetServers[i] + 1);

		// Server

		QString server = g_HBot.GetXMLProfile()->GetProfileValue(QString::null, m_Profile, group, TAG_PROFILE_BATTLENET_SERVER, ATTRIB_PROFILE_BATTLENET_SERVER_NAME);

		if (server.isEmpty())
		{
			g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_BNET, "Battle.net Field <b>Battle.net-" + QString::number(battlenetServers[i] + 1) + "::Server</b> in the Battle.net tab of the profile manager must not be empty. Please change this to a non-empty string and reload the profile.", QString::number(battlenetServers[i] + 1), true);
			continue;
		}

		for (int j = 0; j < m_BNets.size(); ++j)
		{
			if (server == m_BNets[j]->GetServer())
			{
				g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_BNET, "Battle.net Field <b>Battle.net-" + QString::number(battlenetServers[i] + 1) + "::Server</b> in the Battle.net tab of the profile must not be equal to the server of any other server connection. Please change this to a different server and reload the profile.", QString::number(battlenetServers[i] + 1), true);
				continue;
			}
		}

		// Server Alias

		QString serverAlias = g_HBot.GetXMLProfile()->GetProfileValue(QString::null, m_Profile, group, TAG_PROFILE_BATTLENET_SERVER, ATTRIB_PROFILE_BATTLENET_SERVER_ALIAS);

		for (int j = 0; j < m_BNets.size(); ++j)
		{
			if (serverAlias == m_BNets[j]->GetServerAlias())
			{
				g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_BNET, "Battle.net Field <b>Battle.net-" + QString::number(battlenetServers[i] + 1) + "::Server Alias</b> in the Battle.net tab of the profile must not be equal to the server alias of any other server connection. Please change this to a different server alias and reload the profile.", QString::number(battlenetServers[i] + 1), true);
				continue;
			}
		}

		if (serverAlias.isEmpty())
		{
			if (server.toLower() == BATTLENET_USEAST)
			{
				serverAlias = BATTLENET_USEAST_ALIAS;
			}
			else if (server.toLower() == BATTLENET_USWEST)
			{
				serverAlias = BATTLENET_USWEST_ALIAS;
			}
			else if (server.toLower() == BATTLENET_EUROPE)
			{
				serverAlias = BATTLENET_EUROPE_ALIAS;
			}
			else if (server.toLower() == BATTLENET_ASIA)
			{
				serverAlias = BATTLENET_ASIA_ALIAS;
			}
			else
			{
				serverAlias = server;
			}
		}

		if (server.toLower() == BATTLENET_USEAST && serverAlias != BATTLENET_USEAST_ALIAS)
		{
			g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_BNET, "Note: Battle.net Field <b>Battle.net-" + QString::number(battlenetServers[i] + 1) + "::Server</b> is [" + BATTLENET_USEAST + "] and Battle.net Field <b>Battle.net-" + QString::number(battlenetServers[i] + 1) + "::Server Alias</b> is [" + serverAlias + "], but the proper server alias is [" + BATTLENET_USEAST_ALIAS + "].", QString::number(battlenetServers[i] + 1));
		}
		else if (server.toLower() == BATTLENET_USWEST && serverAlias != BATTLENET_USWEST_ALIAS)
		{
			g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_BNET, "Note: Battle.net Field <b>Battle.net-" + QString::number(battlenetServers[i] + 1) + "::Server</b> is [" + BATTLENET_USWEST + "] and Battle.net Field <b>Battle.net-" + QString::number(battlenetServers[i] + 1) + "::Server Alias</b> is [" + serverAlias + "], but the proper server alias is [" + BATTLENET_USWEST_ALIAS + "].", QString::number(battlenetServers[i] + 1));
		}
		else if (server.toLower() == BATTLENET_EUROPE && serverAlias != BATTLENET_EUROPE_ALIAS)
		{
			g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_BNET, "Note: Battle.net Field <b>Battle.net-" + QString::number(battlenetServers[i] + 1) + "::Server</b> is [" + BATTLENET_EUROPE + "] and Battle.net Field <b>Battle.net-" + QString::number(battlenetServers[i] + 1) + "::Server Alias</b> is [" + serverAlias + "], but the proper server alias is [" + BATTLENET_EUROPE_ALIAS + "].", QString::number(battlenetServers[i] + 1));
		}
		else if (server.toLower() == BATTLENET_ASIA && serverAlias != BATTLENET_ASIA_ALIAS)
		{
			g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_BNET, "Note: Battle.net Field <b>Battle.net-" + QString::number(battlenetServers[i] + 1) + "::Server</b> is [" + BATTLENET_ASIA + "] and Battle.net Field <b>Battle.net-" + QString::number(battlenetServers[i] + 1) + "::Server Alias</b> is [" + serverAlias + "], but the proper server alias is [" + BATTLENET_ASIA_ALIAS + "].", QString::number(battlenetServers[i] + 1));
		}

		// RoC CD-Key

		QString roc = g_HBot.GetXMLProfile()->GetProfileValue(QString::null, m_Profile, group, TAG_PROFILE_BATTLENET_CDKEY, ATTRIB_PROFILE_BATTLENET_CDKEY_ROC);
		
		if (roc.count() != 26)
		{
			g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_BNET, "Battle.net Field <b>Battle.net-" + QString::number(battlenetServers[i] + 1) + "::Reign of Chaos CD-Key</b> must be 26 characters long. Please change this to a 26 character string and reload the profile.", QString::number(battlenetServers[i] + 1), true);
			continue;
		}
		
		for (int j = 0; j < 26; ++j)
		{
			if (!roc[j].isLetterOrNumber())
			{
				g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_BNET, "Battle.net Field <b>Battle.net-" + QString::number(battlenetServers[i] + 1) + "::Reign of Chaos CD-Key</b> must contain only alphanumerical characters. Please change this to a valid string and reload the profile.", QString::number(battlenetServers[i] + 1), true);
				continue;
			}
		}

		// TFT CD-Key

		QString tft = g_HBot.GetXMLProfile()->GetProfileValue(QString::null, m_Profile, group, TAG_PROFILE_BATTLENET_CDKEY, ATTRIB_PROFILE_BATTLENET_CDKEY_TFT);
		
		if (m_UsingTFT)
		{
			if (tft.count() != 26)
			{
				g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_BNET, "Battle.net Field <b>Battle.net-" + QString::number(battlenetServers[i] + 1) + "::The Frozen Throne CD-Key</b> must be 26 characters long. Please change this to a 26 character string and reload the profile.", QString::number(battlenetServers[i] + 1), true);
				continue;
			}
		
			for (int j = 0; j < 26; ++j)
			{
				if (!tft[j].isLetterOrNumber())
				{
					g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_BNET, "Battle.net Field <b>Battle.net-" + QString::number(battlenetServers[i] + 1) + "::The Frozen Throne CD-Key</b> must contain only alphanumerical characters. Please change this to a valid string and reload the profile.", QString::number(battlenetServers[i] + 1), true);
					continue;
				}
			}
		}
		
		// Country/Language/CountryAbbrev

		QLocale qLocale;
		QLocale::Language language = (QLocale::Language) g_HBot.GetXMLProfile()->GetProfileValue(qLocale.languageToString(qLocale.language()), m_Profile, group, TAG_PROFILE_BATTLENET_LOCALE, ATTRIB_PROFILE_BATTLENET_LANGUAGE_CODE).toUInt();
		QLocale::Country country = (QLocale::Country) g_HBot.GetXMLProfile()->GetProfileValue(qLocale.countryToString(qLocale.country()), m_Profile, group, TAG_PROFILE_BATTLENET_LOCALE, ATTRIB_PROFILE_BATTLENET_COUNTRY_CODE).toUInt();
		QString countryAbbrev;

		QLocale newLocale(language, country);

		if (language != qLocale.language())
		{
			g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_BNET, "Note: Battle.net Field <b>Battle.net-" + QString::number(battlenetServers[i] + 1) + "::Language Code</b> is [" + QLocale::languageToString(language) + " : " + QString::number(language) + "], but HBot detects that it should be [" + QLocale::languageToString(qLocale.language()) + " : " + QString::number(qLocale.language()) + "].", QString::number(battlenetServers[i] + 1));
		}

		if (country != qLocale.country())
		{
			g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_BNET, "Note: Battle.net Field <b>Battle.net-" + QString::number(battlenetServers[i] + 1) + "::Country Code</b> is [" + QLocale::countryToString(country) + " : " + QString::number(country) + "], but HBot detects that it should be [" + QLocale::countryToString(qLocale.country()) + " : " + QString::number(qLocale.country()) + "].", QString::number(battlenetServers[i] + 1));
			countryAbbrev = newLocale.name().right(2);
		}
		
		countryAbbrev = newLocale.name().right(2);

		// Username

		QString username = g_HBot.GetXMLProfile()->GetProfileValue(QString::null, m_Profile, group, TAG_PROFILE_BATTLENET_LOGIN, ATTRIB_PROFILE_BATTLENET_LOGIN_USERNAME);
		
		if (username.count() < 3)
		{
			g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_BNET, "Battle.net Field <b>Battle.net-" + QString::number(battlenetServers[i] + 1) + "::Username</b> in the Battle.net tab of the profile manager must be at least 3 characters long. Please change this to a valid string and reload the profile.", QString::number(battlenetServers[i] + 1), true);
			continue;
		}
		else if (username.count() > 15)
		{
			g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_BNET, "Battle.net Field <b>Battle.net-" + QString::number(battlenetServers[i] + 1) + "::Username</b> in the Battle.net tab of the profile manager must not be longer than 15 characters. Please change this to a valid string and reload the profile.", QString::number(battlenetServers[i] + 1), true);
			continue;
		}

		// Password

		QString password = g_HBot.GetXMLProfile()->GetProfileValue(QString::null, m_Profile, group, TAG_PROFILE_BATTLENET_LOGIN, ATTRIB_PROFILE_BATTLENET_LOGIN_PASSWORD);
		
		if (password.count() < 3)
		{
			g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_BNET, "Battle.net Field <b>Battle.net-" + QString::number(battlenetServers[i] + 1) + "::Password</b> in the Battle.net tab of the profile manager must be at least 3 characters long. Please change this to a valid string and reload the profile.", QString::number(battlenetServers[i] + 1), true);
			continue;
		}
		else if (password.count() > 15)
		{
			g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_BNET, "Battle.net Field <b>Battle.net-" + QString::number(battlenetServers[i] + 1) + "::Password</b> in the Battle.net tab of the profile manager must not be longer than 15 characters. Please change this to a valid string and reload the profile.", QString::number(battlenetServers[i] + 1), true);
			continue;
		}

		// First Channel

		QString channel = g_HBot.GetXMLProfile()->GetProfileValue(QString::null, m_Profile, group, TAG_PROFILE_BATTLENET_CHANNEL, ATTRIB_PROFILE_BATTLENET_CHANNEL_NAME);

		if (channel.isEmpty())
		{
			g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_BNET, "Battle.net Field <b>Battle.net-" + QString::number(battlenetServers[i] + 1) + "::First Channel</b> in the Battle.net tab of the profile manager must not be empty. Please change this to a non-empty string and reload the profile.", QString::number(battlenetServers[i] + 1), true);
			continue;
		}
		else if (channel.count() > 31)
		{
			g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_BNET, "Battle.net Field <b>Battle.net-" + QString::number(battlenetServers[i] + 1) + "::First Channel</b> in the Battle.net tab of the profile manager must not be longer than 31 characters. Please change this to a valid string and reload the profile.", QString::number(battlenetServers[i] + 1), true);
			continue;
		}

		// Root Admins

		QStringList rootAdmins;

		for (int j = 0; j < g_HBot.GetXMLProfile()->GetProfileValue("0", m_Profile, group, TAG_PROFILE_BATTLENET_ROOTADMIN, ATTRIB_PROFILE_BATTLENET_ROOTADMIN_AMOUNT).toUInt(); ++j)
		{
			QString user = g_HBot.GetXMLProfile()->GetProfileValue(QString::null, m_Profile, group, TAG_PROFILE_BATTLENET_ROOTADMIN, ATTRIB_PROFILE_BATTLENET_ROOTADMIN + QString::number(j + 1));

			if (user.isEmpty())
			{
				g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_BNET, "Battle.net Field <b>Battle.net-" + QString::number(battlenetServers[i] + 1) + "::Root Admins-" + QString::number(j + 1) + "</b> in the Battle.net tab of the profile manager must not be empty. Please change this to a non-empty string and reload the profile.", QString::number(battlenetServers[i] + 1), true);
				continue;
			}
			else if (user.count() < 3)
			{
				g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_BNET, "Battle.net Field <b>Battle.net-" + QString::number(battlenetServers[i] + 1) + "::Root Admins-" + QString::number(j + 1) + "</b> in the Battle.net tab of the profile manager must at least be 3 characters long. Please change this to a valid string and reload the profile.", QString::number(battlenetServers[i] + 1), true);
				continue;
			}
			else if (user.count() > 15)
			{
				g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_BNET, "Battle.net Field <b>Battle.net-" + QString::number(battlenetServers[i] + 1) + "::Root Admins-" + QString::number(j + 1) + "</b> in the Battle.net tab of the profile manager must not be longer than 15 characters. Please change this to a valid string and reload the profile.", QString::number(battlenetServers[i] + 1), true);
				continue;
			}
			else
			{
				rootAdmins.push_back(user);
			}
		}

		// Server Command Trigger

		QString trigger = g_HBot.GetXMLProfile()->GetProfileValue(QString::null, m_Profile, group, TAG_PROFILE_BATTLENET_COMMANDTRIGGER, ATTRIB_PROFILE_BATTLENET_COMMANDTRIGGER);

		if (trigger.isEmpty())
		{
			g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_BNET, "Battle.net Field <b>Battle.net-" + QString::number(battlenetServers[i] + 1) + "::Server Command Trigger</b> in the Battle.net tab of the profile manager must not be empty. Please change this to a valid character and reload the profile.", QString::number(battlenetServers[i] + 1), true);
			continue;
		}
		else if (trigger == "/")
		{
			g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_BNET, "Battle.net Field <b>Battle.net-" + QString::number(battlenetServers[i] + 1) + "::Server Command Trigger</b> in the Battle.net tab of the profile manager must not be the character: <b>/</b>. Please change this to a non-empty string and reload the profile.", QString::number(battlenetServers[i] + 1), true);
			continue;
		}

		// Hold Friends/Hold Clan/Public Commands

		bool holdFriends = g_HBot.GetXMLProfile()->GetProfileValue("1", m_Profile, group, TAG_PROFILE_BATTLENET_HOLDFRIENDS).toUInt();
		bool holdClan = g_HBot.GetXMLProfile()->GetProfileValue("1", m_Profile, group, TAG_PROFILE_BATTLENET_HOLDCLAN).toUInt();
		bool publicCommands = g_HBot.GetXMLProfile()->GetProfileValue("1", m_Profile, group, TAG_PROFILE_BATTLENET_PUBLICCOMMANDS).toUInt();

		g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_BNET, "Validity check for Battle.net-" + QString::number(battlenetServers[i] + 1) + " server configuration was successful.", QString::number(battlenetServers[i] + 1), false);
		
		m_BNets.push_back(new CBNet(battlenetServers[i], server, serverAlias, roc, tft, QLocale::countryToString(country), QLocale::languageToString(language), countryAbbrev, username, password, rootAdmins, trigger[0].toAscii(), holdFriends, holdClan, publicCommands));
	}

	/*
	Run some OperationThread tests. -- Was successful. But I do not want to create these instances in this function
	Instead, they will be created when the bot is 'Ran'. There is no point in having X*2 threads up when the bot
	isn't even running. There will be almost 0 queries being ran when the bot profile is only open except from the
	database tab, and the original DB connection should be able to handle that easily.
	
	OperationQueue *a = new OperationQueue();
	OperationQueue *b = new OperationQueue();

	for (int i = 0; i < NUM_OP_THREADS_X; ++i)
	{
		m_OperationThreadsLowPriority[i] = new OperationThread("LowPriority(" + QString::number(i) + ")", a, QThread::LowPriority);
	}

	for (int i = 0; i < NUM_OP_THREADS_X; ++i)
	{
		m_OperationThreadsNormalPriority[i] = new OperationThread("NormalPriority(" + QString::number(i) + ")", b, QThread::NormalPriority);
	}
	
	a->Enqueue(NULL, &PrintTest);
	b->Enqueue(NULL, &PrintTest);
	a->Enqueue(NULL, &PrintTest);
	a->Enqueue(NULL, &PrintTest);
	b->Enqueue(NULL, &PrintTest);
	a->Enqueue(NULL, &PrintTest);
	a->Enqueue(NULL, &PrintTest);
	b->Enqueue(NULL, &PrintTest);
	b->Enqueue(NULL, &PrintTest);
	*/
}

CHBot::~CHBot()
{
	if (m_Database)
	{
		/*
		TODO:
		Remove while loop below and set a variable to check after program has entered main execution loop.
		*/
		while (m_Database->isRunning()) { }
		delete m_Database;
	}

	if ((m_LogDevice != NULL) && (m_LogDevice->isOpen() == true))
	{
		m_LogDevice->close();
		delete m_LogDevice;
		m_LogDevice = NULL;
	}

	if (m_Log != NULL)
	{
		delete m_Log;
		m_Log = NULL;
	}
}

QVector<int> CHBot::GetNumberBnet()
{
	QVector<int> battlenetServers;

	for (int i = 0; i < m_BNets.size(); ++i)
	{
		battlenetServers.push_back(m_BNets[i]->GetCounterID());
	}

	return battlenetServers;
}

void SignalInt(int s)
{
	signal(SIGINT, SignalInt2);

	g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_SIGNAL, "Caught signal [" + QString::number(s) + "] - Closing profile nicely");

	if (g_HBotClassPtr != NULL)
	{
		g_HBotClass.SetExitingNice(true);
	}
	else
	{
		g_HBot.CloseProfile();
	}
}

void SignalInt2(int s)
{
	g_HBot.PrintConsole(PRINT_CONSOLE_GROUP_SIGNAL, "Caught signal [" + QString::number(s) + "] - Closing profile immediately");

	if (g_HBotClassPtr != NULL)
	{
		if (g_HBotClass.GetExiting())
		{
			exit(1);
		}
		else
		{
			g_HBotClass.SetExiting(true);
		}
	}
	else
	{
		g_HBot.CloseProfile();
	}
}

unsigned int GetMilliseconds()
{
	return g_HBotClass.GetTime().elapsed();
}

unsigned int GetSeconds()
{
	return GetMilliseconds() / 1000;
}
